package top.jle.wx.proxy.feign.redPacket;

import com.alibaba.fastjson2.JSONObject;
import jakarta.annotation.PostConstruct;
import top.jle.jpa.basic.exception.BasicCustomException;
import top.jle.wx.proxy.exception.ExceptionCode;
import cn.hutool.cache.CacheUtil;
import cn.hutool.cache.impl.TimedCache;
import org.apache.http.*;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.DefaultHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicHeaderElementIterator;
import org.apache.http.protocol.HTTP;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;
import org.json.XML;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Component;
import top.jle.wx.proxy.domain.redpack.*;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.security.*;
import java.time.Duration;

@Component
public class RedPacketAPIFeign {
    private static final Logger LOGGER = LoggerFactory.getLogger(RedPacketAPIFeign.class);
    private static final TimedCache<String,CloseableHttpClient> clientCache = CacheUtil.newTimedCache(Duration.ofSeconds(30L).toMillis());
    private static final String SEND_RED_PACK_API_URL = "https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack";
    private static final String GET_RED_PACK_INFO_URL = "https://api.mch.weixin.qq.com/mmpaymkttransfers/gethbinfo";
    private static final String TRANSFER_API_URL = "https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers";
    private static final String GET_TRANSFER_API_URL = "https://api.mch.weixin.qq.com/mmpaymkttransfers/gettransferinfo";
    @Value("${ssl.key-store.root}")
    private Resource keyStoreRoot;

    @PostConstruct
    public void initCacheSchedule(){
        LOGGER.info("initCacheSchedule...");
        clientCache.schedulePrune(Duration.ofSeconds(30L).toMillis());
    }
    public JSONObject getRedPackInfo(GetRedPackInfoDTO dto) throws Exception {
        return this.httpRequest(dto,dto.getMch_id(),GET_RED_PACK_INFO_URL);
    }

    public JSONObject sendRedPack(SendRedPackDTO dto) throws Exception {
        return this.httpRequest(dto,dto.getMch_id(),SEND_RED_PACK_API_URL);
    }

    public JSONObject getTransferInfo(GetTransferInfoDTO dto) throws Exception {
        return this.httpRequest(dto,dto.getMch_id(),GET_TRANSFER_API_URL);
    }

    public JSONObject sendTransfer(TransferDTO dto) throws Exception {
        return this.httpRequest(dto,dto.getMchid(),TRANSFER_API_URL);
    }

    private JSONObject httpRequest(NeedSignedDTO dto, String mchId, String url) throws Exception {
        CloseableHttpClient client = getClient(mchId);
        HttpPost post = new HttpPost(url);

        String requestXML = XML.toString(new org.json.JSONObject(dto),"xml");
        post.setEntity(new StringEntity(requestXML, Charset.defaultCharset()));
        CloseableHttpResponse response = null;
        JSONObject result = null;
        try {
            response = client.execute(post);
            HttpEntity entity = response.getEntity();
            if(entity!=null){
                String xml = EntityUtils.toString(entity,Charset.defaultCharset());
                EntityUtils.consume(entity);
                result = JSONObject.parseObject(XML.toJSONObject(xml).getJSONObject("xml").toString());
            }

        } catch (IOException e) {
            LOGGER.error(e.getMessage(),e);
        }finally {
            try {
                if(response!=null){
                    response.close();
                }

            } catch (IOException e) {
                LOGGER.error(e.getMessage(),e);
            }
        }
        return result;
    }

    private CloseableHttpClient getClient(String mchId) throws Exception {

      //  ClientCacheManager manager = ClientCacheManager.getInstance();
        LOGGER.info("获取Client:{}",mchId);
        CloseableHttpClient client = clientCache.get(mchId);

        if(client==null){
            LOGGER.info("client不存在");
            Resource sslResource = new FileSystemResource(keyStoreRoot.getFile().getPath() + File.separator + mchId + File.separator + "apiclient_cert.p12");
            LOGGER.info("client 证书文件:"+sslResource.getURI());
            if (sslResource.exists()) {
                LOGGER.info("开始构建Client");
                KeyStore keyStore = KeyStore.getInstance("PKCS12");
                keyStore.load(sslResource.getInputStream(), mchId.toCharArray());
                // 实例化密钥库 & 初始化密钥工厂
                KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
                kmf.init(keyStore, mchId.toCharArray());
                // 创建 SSLContext
                SSLContext sslContext = SSLContext.getInstance("TLS");
                sslContext.init(kmf.getKeyManagers(), null, new SecureRandom());

                SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(
                        sslContext,
                        new String[]{"TLSv1.2"},
                        null,
                        new DefaultHostnameVerifier());

                PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(RegistryBuilder.<ConnectionSocketFactory>create()
                        .register("http", PlainConnectionSocketFactory.getSocketFactory())
                        .register("https", sslConnectionSocketFactory)
                        .build());
                connManager.setMaxTotal(1000);
                connManager.setDefaultMaxPerRoute(100);
                client = HttpClients.custom()
                        .setKeepAliveStrategy((HttpResponse response, HttpContext context)->{
                            //设置请求为keep-alive模式的长链接以便复用
                            //达到短时间内容重复调用接口而不用每次都实例化一个client加载证书
                            HeaderElementIterator it = new BasicHeaderElementIterator
                                    (response.headerIterator(HTTP.CONN_KEEP_ALIVE));
                            while (it.hasNext()) {
                                HeaderElement he = it.nextElement();
                                String param = he.getName();
                                String value = he.getValue();
                                if (value != null && param.equalsIgnoreCase
                                        ("timeout")) {
                                    return Long.parseLong(value) * 1000;
                                }
                            }
                            return 30 * 1000;//如果没有约定，则默认定义时长为30s
                        })
                        .setConnectionManager(connManager)
                        .build();
                //将构建好的client纳入缓存
                clientCache.put(mchId,client);
                LOGGER.info("client构建完成");
            }else{
                throw new BasicCustomException(ExceptionCode.MCH_CERTIFICATE_NOT_EXISTS);
            }
        }

        return client;
    }
}
